package com.yy.wga.admin.service.common.interceptor;

import cn.hutool.core.net.URLDecoder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import com.yy.wga.common.core.constants.GlobalConstant;
import com.yy.wga.common.core.enmus.EResponseCode;
import com.yy.wga.common.core.exception.BusinessException;
import com.yy.wga.common.util.common.EnvUtils;
import com.yy.wga.common.util.common.JsonUtils;
import com.yy.wga.common.util.common.StringUtils;
import com.yy.wga.common.util.sign.SignUtils;
import com.yy.wga.mp.common.contants.MpConstant;
import com.yy.wga.mp.common.dto.UserCacheDTO;
import com.yy.wga.mp.common.enmus.EOauthRespCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @Author: gc
 * @Date: 2024/10/22
 * @Description:
 **/
@Slf4j
@Component
@RequiredArgsConstructor
public class RequestUserInterceptor implements HandlerInterceptor {

    @Value("${spring.profiles.active}")
    private String profiles;

    @Value(value = "${white.url:}")
    private List<String> whiteUrlList;

    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", request.getMethod());
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        log.info("url:{}，", request.getServletPath());
        String authorizationHeader = request.getHeader("Authorization");
        log.info("Authorization:{}，", authorizationHeader);

        if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) {
            return false;
        }
        // 运营后台白名单
        for (String whiteUrl : whiteUrlList) {
            if (whiteUrl.endsWith("*")) {
                whiteUrl =StringUtils.removeSuffix(whiteUrl, "*");
                if (request.getServletPath().startsWith(whiteUrl)) {
                    return true;
                }
            } else {
                if (request.getServletPath().equals(whiteUrl)) {
                    return true;
                }
            }
        }

        if (StringUtils.isBlank(authorizationHeader)) {
            throw new BusinessException(EResponseCode.ILLEGAL_REQUEST);
        }
        authorizationHeader = URLDecoder.decode(authorizationHeader, Charsets.UTF_8);
        authorizationHeader = authorizationHeader.replace("Bearer ","");
        JSONObject authorizationJson = JSON.parseObject(authorizationHeader);

        // 用户登录信息
        // token校验
        String token = authorizationJson.getString("token");
        String secKey = null;
        if (StringUtils.isNotBlank(token)) {
            UserCacheDTO userCacheDTO = this.getUserCacheDTO(token);
            if (userCacheDTO == null) {
                throw new BusinessException(EOauthRespCode.UNAUTHORIZED);
            }
            secKey = userCacheDTO.getSessionKey();
            RequestUserHolder.setUserCache(userCacheDTO);
        }

        // 签名校验
        Map<String, Object> paramsMap = new HashMap<>(authorizationJson);
        // 获取请求体内容
        String methodType = request.getMethod().toUpperCase();
        if (("POST".equals(methodType)) || ("DELETE".equals(methodType)) || ("PUT".equals(methodType))) {
            String data = this.getInputStream(request);
            // 非json内容不处理
            if (JsonUtils.isJsonString(data)) {
                // 通过请求头的token获取用户信息设置进请求体
                paramsMap.putAll(JsonUtils.parseObject(data, Map.class));
            }
        }
        paramsMap.putAll(this.getParameterMap(request));

        log.info("请求参数:{}，contentType:{},methodType:{}", JsonUtils.toJSONString(paramsMap), request.getHeader(HttpHeaders.CONTENT_TYPE), methodType);
        // 加密校验
//        if (!("test".equals(profiles) || "dev".equals(profiles)) || !authorizationJson.getBooleanValue("noSign")) {
//            this.validate(paramsMap, secKey);
//        }
        return true;
    }

    /**
     * 根据token获取用户信息缓存
     *
     * @param token token
     * @return
     */
    private UserCacheDTO getUserCacheDTO(String token) {
        if (StringUtils.isNotBlank(token)) {
            String userInfoKey = MpConstant.USER_INFO + token;
            String userInfoJson = stringRedisTemplate.opsForValue().get(userInfoKey);
            if (StringUtils.isBlank(userInfoJson)) {
                throw new BusinessException(EOauthRespCode.SESSION_ID_INVALID);
            }
            return JsonUtils.parseObject(userInfoJson, UserCacheDTO.class);
        }
        return null;
    }


    /**
     * 获取请求流参数
     *
     * @param request
     * @return
     */
    private String getInputStream(HttpServletRequest request) throws IOException {
        return IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8.name());
    }

    /**
     * 将request中的Mep转换成map
     *
     * @param request
     * @return
     */
    public Map<String, Object> getParameterMap(HttpServletRequest request) {
        // 参数Map
        Map<String, String[]> properties = request.getParameterMap();
        // 返回值Map
        Map<String, Object> returnMap = new HashMap<>();
        Iterator<Map.Entry<String, String[]>> entries = properties.entrySet().iterator();
        Map.Entry<String, String[]> entry;
        String name = "";
        String value = "";
        while (entries.hasNext()) {
            entry = entries.next();
            name = entry.getKey();
            String[] valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else if (valueObj.length > 1) {
                value = Arrays.toString(valueObj);
            } else {
                value = valueObj[0];
            }
            returnMap.put(name, value);
        }
        return returnMap;
    }

    /**
     * 加密校验
     *
     * @param paramsMap 请求参数
     * @param secKey    密钥
     */
    private void validate(Map<String, Object> paramsMap, String secKey) {
        boolean validSign = SignUtils.isValidSign(paramsMap, secKey);
        if (!validSign) {
            String profiles = EnvUtils.get("spring.profiles.active");
            if (GlobalConstant.ENV_DEV.equals(profiles) || GlobalConstant.ENV_TEST.equals(profiles)) {
                String signStr = SignUtils.getSignStr(paramsMap, secKey);
                throw new BusinessException(EResponseCode.SYSTEM_SIGN_ERROR.getCode(), "######服务端拼接kv：" + SignUtils.signOriginal(paramsMap, secKey) + "\n###服务端加密sign:" + signStr);
            }
            throw new BusinessException(EResponseCode.SYSTEM_SIGN_ERROR);
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        RequestUserHolder.removeUserCache();
    }
}
